#include "include/htmledit.h"
#include "ui_htmledit.h"

#include <QFileDialog>
#include <QUrl>
#include <QImageReader>
#include <QtGlobal>
#include <QTextBlock>
//#include <stack>
#include <vector>
#include <algorithm>
#include <functional>
#include <QResizeEvent>
#include <QTextLayout>
#include <QAbstractTextDocumentLayout>
#include <QTextBlockFormat>
#include <QTextBlock>
#include <QSettings>
#include <QDebug>
#include "include/RichText.h"


//const QString HtmlEdit::DefaultImageResourceName = "card://image";
const QString HtmlEdit::DefaultImageResourceName = "untitled-image";


namespace
{


//loading image from a file applying size restrictions
	QImage loadImage(QString path) //QImage can be freely copied by value
	{
		const int maxWidth = QSettings().value("maxImageWidth", 300).toInt();
		const int maxHeight = QSettings().value("maxImageHeight", 300).toInt();

		QImage image = QImageReader(path).read();

		if (image.width() <= maxWidth && image.height() <= maxWidth)
			return image;

		return image.scaled(maxWidth, maxHeight, Qt::KeepAspectRatio, Qt::SmoothTransformation);
	}


//remove QTextDocument element at give position
	void removePosition(QTextDocument* pDoc, int position)
	{
		QTextCursor c(pDoc);
		c.setPosition(position, QTextCursor::MoveAnchor);
		c.movePosition(QTextCursor::NextCharacter, QTextCursor::KeepAnchor);
		c.removeSelectedText();
	}


//fine all image elements in QTextDocument
	template <class Container>
	void findAllImages(QTextDocument* pDoc, Container& container)
	{
		for (QTextBlock it = pDoc->begin(); it != pDoc->end(); it = it.next())
		{
			for (QTextBlock::iterator it2 = it.begin(); it2 != it.end(); ++it2)
			{
				QTextFragment fragment = it2.fragment();
				if (fragment.charFormat().isImageFormat())
				{
					container.push_back(fragment.position());
				}
			}
		}
	}


//removes all existing images in QTextDocument
//appends the image
	void insertImage(QTextEdit* textEdit, QImage image, const QString& imageResourceName)
	{
		QTextDocument* pDoc = textEdit->document();

		pDoc->addResource( QTextDocument::ImageResource, QUrl(imageResourceName), QVariant ( image ) );

		//removing all existing images
		std::vector<int> stack;
		findAllImages(pDoc, stack);
		std::for_each(stack.rbegin(), stack.rend(), std::bind1st(std::ptr_fun(removePosition), pDoc));


		//QTextCursor cursor(pDoc);
		//cursor.movePosition(QTextCursor::End);

		QTextCursor cursor = textEdit->textCursor();

		QTextImageFormat imageFormat;
		imageFormat.setWidth( image.width() );
		imageFormat.setHeight( image.height() );
		imageFormat.setName( imageResourceName );
		cursor.insertImage(imageFormat);
	}

//removes all images excepting the last one. Used when control is switched to read only mode
	void removeAllImagesButLast(QTextDocument* pDoc)
	{
		std::vector<int> stack;
		findAllImages(pDoc, stack);
		if (stack.size() < 2)
			return;

		stack.pop_back();
		std::for_each(stack.rbegin(), stack.rend(), std::bind1st(std::ptr_fun(removePosition), pDoc));
	}


	bool focusIsInside(QObject* obj)
	{
		QWidget* widget = dynamic_cast<QWidget*>(obj);
		if (widget != 0 && widget->hasFocus())
			return true;

		foreach (QObject* child, obj->children())
		{
			if (focusIsInside(child))
				return true;
		}

		return false;
	}

}


struct HtmlEdit::FocusCapturer
{
	FocusCapturer(HtmlEdit* widget) : _widget(widget)
	{
		_savFocusIsCaptured = _widget->m_focusIsCaptured;
		_widget->m_focusIsCaptured = true;
	}

	~FocusCapturer()
	{
		_widget->m_focusIsCaptured = _savFocusIsCaptured;
	}

private:

	bool _savFocusIsCaptured;
	HtmlEdit* _widget;
};





HtmlEdit::HtmlEdit(QWidget *parent) :
	QWidget(parent),
	ui(new Ui::HtmlEdit),
	m_focusIsCaptured(false) //normally focus is not captured, this happens only when dialogs are summoned from the class
{
	ui->setupUi(this);

//	ui->insertImageButton->setIcon(QIcon::fromTheme("insert-image"));

	m_imageResourceName = DefaultImageResourceName;

	m_pDocument = new QTextDocument(this);

	toEditMode();
	connect(ui->insertImageButton, SIGNAL(clicked()), this, SLOT(slot_addImage()));

	ui->textEdit->installEventFilter(this);
	ui->insertImageButton->installEventFilter(this);
	ui->richTextView->installEventFilter(this);
}


HtmlEdit::~HtmlEdit()
{
	delete ui;
}


QString HtmlEdit::imageResourceName() const
{
	return m_imageResourceName;
}

void HtmlEdit::setImageResourceName(const QString& value)
{
	m_imageResourceName = value;
}

Qt::AlignmentFlag HtmlEdit::horizontalAlignment() const
{
	return 	m_horizontalAlignment;
}

void HtmlEdit::setHorizontalAlignment(Qt::AlignmentFlag value)
{
	if (horizontalAlignment() == value)
		return;

	m_horizontalAlignment = value;

	RichText::alignAllText(m_pDocument, value);
}

bool HtmlEdit::isReadOnly() const
{
	return m_isReadOnly;
}


void HtmlEdit::setReadOnly(bool value)
{
	if (isReadOnly() == value)
		return;

	if (value)
		toReadOnlyMode();
	else
		toEditMode();

	emit readOnlyChanged(value);
}



void HtmlEdit::setContentIsVisible(bool value)
{
	ui->richTextView->setContentIsVisible(value);
}


//for internal use only
void HtmlEdit::toReadOnlyMode()
{
	m_isReadOnly = true;

	removeAllImagesButLast(m_pDocument);
	ui->richTextView->setDocument(m_pDocument);
	ui->stackedWidget->setCurrentWidget(ui->pageReadOnlyMode);
	setFocusProxy(ui->richTextView);

	emit editModeExited();
}


//for internal use only
void HtmlEdit::toEditMode()
{
	m_isReadOnly = false;

	ui->textEdit->setDocument(m_pDocument);
	ui->stackedWidget->setCurrentWidget(ui->pageEditMode);
	setFocusProxy(ui->textEdit);

//	if (m_pDocument != 0)
//	{
//		QTextCursor c(m_pDocument);
//		c.movePosition(QTextCursor::End);
//	}
}


void HtmlEdit::slot_addImage()
{
	FocusCapturer focusCapturer(this); //disallow focus withdrawing

	QString filename = QFileDialog::getOpenFileName(this, tr("Mu, Add Image File"), ".", tr("Image Files (*.png *.jpg *.jpeg *.bmp)"));

	setFocus(); //setting focus back to the QTextEdit

	if (filename.isEmpty())
		return;

	QImage image = loadImage(filename);

	insertImage(ui->textEdit, image, imageResourceName());
}


void HtmlEdit::clear()
{
	m_pDocument->clear();
}


void HtmlEdit::setImageResource(QImage image)
{
	m_pDocument->addResource( QTextDocument::ImageResource, QUrl(imageResourceName()), QVariant ( image ) );
}


void HtmlEdit::setHtml(QString html)
{
	m_pDocument->setHtml(html);

	RichText::alignAllText(m_pDocument, horizontalAlignment());
}


QTextDocument* HtmlEdit::document() const
{
	return m_pDocument;
}


void HtmlEdit::setDocument(QTextDocument* value)
{
	if (m_pDocument == value)
		return;

	if (m_pDocument != 0 && m_pDocument->parent() == this)
	{
		ui->textEdit->setDocument(0);
		ui->richTextView->setDocument(0);
		delete m_pDocument;
	}

	m_pDocument = value;

	ui->richTextView->setDocument(m_pDocument);
	ui->textEdit->setDocument(m_pDocument);
}




void HtmlEdit::addAction(QAction *action) //shortcuts did not work without it. More elegant solution is required
{
	ui->textEdit->addAction(action);
	ui->richTextView->addAction(action);
}


bool HtmlEdit::eventFilter(QObject * target, QEvent * evt)
{
	if (evt->type() == QEvent::FocusOut)
	{
		if (m_focusIsCaptured)  //focus is captured for the time a dialog (e.g. image loading) is displayed
			return true;

		if (target == ui->textEdit) //we lose focus when user presses insertImage button. this is bad
		{
			if (!focusIsInside(this))
			{
				emit focusOut();
				return true;
			}
		}
		if (target == ui->insertImageButton)
		{
			if (!focusIsInside(this))
			{
				emit focusOut();
				return true;
			}
		}
	}

	return QWidget::eventFilter(target, evt);
}


bool HtmlEdit::isEditMode() const
{
	return !isReadOnly();
}

void HtmlEdit::enterEditMode()
{
	setReadOnly(false);
}

void HtmlEdit::exitEditMode()
{
	setReadOnly(true);
}
